/*
 * VistaCompuerta.cpp
 *
 *  Created on: 25/11/2009
 *      Author: luis
 */

#include <sstream>
#include "VistaCompuerta.h"
#include "AreaDibujo.h"
#include <pangomm.h>
#include <cairomm-1.0/cairomm/cairomm.h>

VistaCompuerta::VistaCompuerta(Circuito* circuito) {
	this->circuito = circuito;
}


void VistaCompuerta::repintar_compuertas_en_context(Cairo::RefPtr<Cairo::Context> cr){
	unsigned int i;
	std::list< Compuerta* >lista_compuerta = circuito->get_lista_compuertas();
	std::list< Compuerta* >::iterator it = lista_compuerta.begin();
	//Recorro la lista de compuertas y pinto
	for (i = 0; i < lista_compuerta.size(); i++){
		pintar_compuerta((*it), cr);
		it++;
	}

}


void VistaCompuerta::repintar_entrada(Cairo::RefPtr<Cairo::Context> cr){
	unsigned int i;
	std::list< Entrada* >lista_entrada = circuito->get_lista_entradas();
	std::list< Entrada* >::iterator it = lista_entrada.begin();
	//Recorro la lista de entradas y pinto
	for (i = 0; i < lista_entrada.size(); i++){
		pintar_compuerta((*it), cr);
		it++;
	}
}

void VistaCompuerta::repintar_salida (Cairo::RefPtr<Cairo::Context> cr){
	unsigned int i;
	std::list< Salida* >lista_salida = circuito->get_lista_salidas();
	std::list< Salida* >::iterator it = lista_salida.begin();
	//Recorro la lista de salidas y pinto
	for (i = 0; i < lista_salida.size(); i++){
		pintar_compuerta((*it), cr);
		it++;
	}

}

void VistaCompuerta::repintar_elem_externos(Cairo::RefPtr<Cairo::Context> cr){
	unsigned int i;
	std::list< ElementoExterno* >lista_elementos = circuito->get_lista_elementos_externos();
	std::list< ElementoExterno* >::iterator it = lista_elementos.begin();
	//Recorro la lista de elementos externos y pinto
	for (i = 0; i < lista_elementos.size(); i++){
		pintar_compuerta((*it), cr);
		it++;
	}

}


void VistaCompuerta::pintar_and(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+30, y+TAMANIO-1);
	cr->line_to(x+1, y+TAMANIO-1);
	cr->line_to(x+1, y+1);
	cr->line_to(x+30, y+1);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();

	cr->move_to(x+30, y+1);
	cr->curve_to(x+30, y+1, x+65, y+TAMANIO/2, x+30, y+TAMANIO-1);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();

	cr->stroke();

}

void VistaCompuerta::pintar_or(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+1, y+1);
	cr->curve_to(x+1, y+1, x+15, y+TAMANIO/2, x+1, y+TAMANIO-1);
	cr->curve_to(x+1, y+TAMANIO-1, x+45, y+45, x+TAMANIO-1, y+TAMANIO/2);
	cr->curve_to(x+TAMANIO-1, y+TAMANIO/2, x+45, y+5, x+1, y+1);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();


	cr->stroke();
}

void VistaCompuerta::pintar_not(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+1, y+1);
	cr->line_to(x+1, y+TAMANIO);
	cr->line_to(x+35, y+(TAMANIO/2));
	cr->line_to(x+1, y+1);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();


	cr->move_to(x+43, y+(TAMANIO/2));
	cr->arc(x+39, y+(TAMANIO/2), 4, M_2_PI, 0);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();


	cr->line_to(x+TAMANIO, y+(TAMANIO/2));
	cr->stroke();
}

void VistaCompuerta::pintar_xor(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+1, y+1);
	cr->curve_to(x+1, y+1, x+15, y+TAMANIO/2, x+1, y+TAMANIO-1);

	cr->move_to(x+5, y+1);
	cr->curve_to(x+5, y+1, x+20, y+TAMANIO/2, x+5, y+TAMANIO-1);
	cr->curve_to(x+5, y+TAMANIO-1, x+45, y+45, x+TAMANIO-1, y+TAMANIO/2);
	cr->curve_to(x+TAMANIO-1, y+TAMANIO/2, x+45, y+5, x+5, y+1);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();


	cr->stroke();
}

void VistaCompuerta::pintar_in(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+20, y+(TAMANIO/2));
	cr->arc(x+11, y+(TAMANIO/2), 9, M_2_PI, 0);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();

	cr->line_to(x+TAMANIO, y+(TAMANIO/2));
	cr->stroke();
}

void VistaCompuerta::pintar_out(int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(2);
	cr->move_to(x+TAMANIO-1, y+(TAMANIO/2));
	cr->arc(x+40, y+(TAMANIO/2), 9, M_2_PI, 0);

	cr->save();
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill_preserve();
	cr->restore();


	cr->move_to(x+31, y+(TAMANIO/2));
	cr->line_to(x, y+(TAMANIO/2));
	cr->stroke();
}

void VistaCompuerta::pintar_compuerta(ElementoCircuital* elemento, Cairo::RefPtr<Cairo::Context> cr){
	int auxiliar = elemento->get_tipo();
	int i;
	if (elemento->get_angulo()==0)
		i=0;
	if (elemento->get_angulo()==90)
		i=1;
	if (elemento->get_angulo()==180)
		i=2;
	if (elemento->get_angulo()==270)
		i=3;
	//Roto lo necesario para que aparezca correctamente la imagen
	if (auxiliar!=TIPO_EXTERNO){
		cr->save();
		cr->translate (elemento->get_posicion_x()+25, elemento->get_posicion_y()+25);
		cr->rotate ((M_PI/2)*i);
		cr->translate (-elemento->get_posicion_x()-25, -elemento->get_posicion_y()-25);
	}
	else{
		ElementoExterno* aux = (dynamic_cast < ElementoExterno* >(elemento));
		cr->save();
		int centro_rotacion_x;
		int centro_rotacion_y;
		aux->get_centro(&centro_rotacion_x, &centro_rotacion_y);
		cr->translate(centro_rotacion_x, centro_rotacion_y);
		cr->rotate((M_PI/2)*i);
		cr->translate(-centro_rotacion_x, -centro_rotacion_y);
	}
	if (auxiliar == TIPO_AND){
		pintar_and(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_NOT){
		pintar_not(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_XOR){
		pintar_xor(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_OR){
		pintar_or(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_ENTRADA){
		pintar_in(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_SALIDA){
		pintar_out(elemento->get_posicion_x(), elemento->get_posicion_y(), cr);
	}
	if (auxiliar == TIPO_EXTERNO){
		ElementoExterno* aux = (dynamic_cast < ElementoExterno* >(elemento));
		pintar_caja_negra(aux->get_cant_entradas(), aux->get_cant_salidas(), aux->get_pos_x_repaint(),
				aux->get_pos_y_repaint(), cr);
	}
	cr->restore();
}

void VistaCompuerta::repintar_texto(Gtk::DrawingArea* area){
	Cairo::RefPtr<Cairo::Context> cr = area->get_window()->create_cairo_context();
	unsigned int i;
	std::list<Entrada*> lista_entradas = circuito->get_lista_entradas();
	std::list<Entrada*>::iterator it_lista_entradas = lista_entradas.begin();
	for (i=0; i<lista_entradas.size(); i++){
		//Repinto los nombres de las entradas
		pintar_nombre_entrada((*it_lista_entradas)->get_posicion_x(),
				(*it_lista_entradas)->get_posicion_y(), area, (*it_lista_entradas));
		it_lista_entradas++;
	}
	std::list<Salida*> lista_salidas = circuito->get_lista_salidas();
	std::list<Salida*>::iterator it_lista_salidas = lista_salidas.begin();
	for (i=0; i<lista_salidas.size(); i++){
		//Repinto los nombres de las salidas
		pintar_nombre_salida((*it_lista_salidas)->get_posicion_x(),
		(*it_lista_salidas)->get_posicion_y(), area, (*it_lista_salidas));
		it_lista_salidas++;
	}
	std::list<Compuerta*> lista_compuertas = circuito->get_lista_compuertas();
	std::list<Compuerta*>::iterator it_lista_compuerta = lista_compuertas.begin();
	for (i=0; i<lista_compuertas.size(); i++){
		//Repinto el tiempo de transicion para las compuertas
		pintar_tiempo_transicion((*it_lista_compuerta)->get_posicion_x(),
		(*it_lista_compuerta)->get_posicion_y(), area, (*it_lista_compuerta));

		it_lista_compuerta++;
	}
}


void VistaCompuerta::pintar_caja_negra(int cant_entradas, int cant_salidas, int x, int y, Cairo::RefPtr<Cairo::Context> cr){
	int altura;
	if (cant_entradas>cant_salidas)
		altura = cant_entradas*(TAMANIO);
	else
		altura = cant_salidas*(TAMANIO);
	int division_entradas = altura / cant_entradas;
	division_entradas = division_entradas/2;
	int division_salidas = altura / cant_salidas;
	division_salidas = division_salidas /2;
	cr->set_source_rgb(0,0,0);
	cr->set_line_width(4);
	cr->rectangle(x+15, y, 70, altura);
	cr->stroke_preserve();
	//cr->stroke();
	cr->set_line_width(2);
	cr->set_source_rgb(0.9, 0.9, 0.9);
	cr->fill();
	cr->stroke_preserve();
	int i;
	int aux = y + division_entradas;

	//DIBUJO ENTRADAS
	cr->set_source_rgb(0,0,0);
	for (i=0; i<cant_entradas; i++){
		cr->move_to(x, aux);
		cr->line_to(x+15, aux);
		cr->stroke();
		aux = aux + (division_entradas*2);
	}
	//DIBUJO SALIDAS

	aux = y + division_salidas;
	for (i=0; i<cant_salidas; i++){
		cr->move_to(x+85+15, aux);
		cr->line_to(x+85, aux);
		cr->stroke();
		aux = aux + division_salidas*2;
	}
	cr->move_to(x+15, y);
	cr->line_to(x+85, y+altura);
	cr->move_to(x+85, y);
	cr->line_to(x+15, y + altura);
	cr->stroke();
}


void VistaCompuerta::pintar_nombre_entrada (int x, int y, Gtk::DrawingArea* area, Entrada* entrada){
	Pango::FontDescription *timesnr_12;
	timesnr_12 = new Pango::FontDescription("Times new roman 12");
	Glib::RefPtr<Pango::Layout> layout = area->create_pango_layout("");
	layout->set_font_description(*timesnr_12);
	std::string texto;
	texto.append(entrada->get_nombre());
	layout->set_markup(texto);
	area->get_window()->draw_layout(
			Gdk::GC::create(area->get_window()),
			x, y+TAMANIO, layout);
	delete timesnr_12;
}

void VistaCompuerta::pintar_nombre_salida (int x, int y, Gtk::DrawingArea* area, Salida* salida){
	Pango::FontDescription *timesnr_12;
	timesnr_12 = new Pango::FontDescription("Times new roman 12");
	Glib::RefPtr<Pango::Layout> layout = area->create_pango_layout("");
	layout->set_font_description(*timesnr_12);
	std::string texto;
	texto.append(salida->get_nombre());
	layout->set_markup(texto);
	area->get_window()->draw_layout(
			Gdk::GC::create(area->get_window()),
			x, y+TAMANIO, layout);
	delete timesnr_12;
}

void VistaCompuerta::pintar_tiempo_transicion (int x, int y, Gtk::DrawingArea* area, Compuerta* compuerta){
	Pango::FontDescription *timesnr_12;
	timesnr_12 = new Pango::FontDescription("Times new roman 12");
	Glib::RefPtr<Pango::Layout> layout = area->create_pango_layout("");
	layout->set_font_description(*timesnr_12);
	std::string texto;
	std:: stringstream flujo;
	flujo << compuerta->get_tiempo();
	texto.append(flujo.str());
	layout->set_markup(texto);
	area->get_window()->draw_layout(
			Gdk::GC::create(area->get_window()),
			x, y+TAMANIO, layout);
	delete timesnr_12;
}




void VistaCompuerta::repintar_compuertas(Gtk::DrawingArea* area){

	Cairo::RefPtr<Cairo::Context> cr = area->get_window()->create_cairo_context();
	repintar_compuertas_en_context(cr);
	repintar_elem_externos(cr);
	repintar_entrada(cr);
	repintar_salida(cr);
}

void VistaCompuerta::pintar_compuerta(int x, int y, Gtk::DrawingArea* area, int tipo){
	Cairo::RefPtr<Cairo::Context> cr = area->get_window()->create_cairo_context();
	if (tipo == COMPUERTA_AND){
		pintar_and(x, y, cr);
	}
	if (tipo == COMPUERTA_OR){
		pintar_or(x, y, cr);
	}
	if (tipo == COMPUERTA_NOT){
		pintar_not(x, y, cr);
	}
	if (tipo == COMPUERTA_XOR){
		pintar_xor(x, y, cr);
	}
	if (tipo == IN){
		pintar_in(x, y, cr);
	}
	if (tipo == OUT){
		pintar_out(x, y, cr);
	}
}

ElementoCircuital* VistaCompuerta::get_elemento(int x, int y){
	ElementoCircuital* auxiliar = NULL;
	auxiliar = get_entrada(x, y);
	if (auxiliar == NULL){
		auxiliar = get_salida(x, y);
	}
	if (auxiliar == NULL){
		auxiliar = get_compuerta(x, y);
	}
	if (auxiliar == NULL){
		auxiliar = get_elem_externo(x, y);
	}
	return auxiliar;
}

Compuerta* VistaCompuerta::get_compuerta(int x, int y){
	Compuerta* auxiliar = NULL;
	unsigned int i;
	std::list< Compuerta* > lista_compuerta = circuito->get_lista_compuertas();
	std::list< Compuerta* >::iterator it = lista_compuerta.begin();
	bool encontrado = false;
	for (i = 0; ((i < lista_compuerta.size())&&(!encontrado)); i++){
		//Si x, y, estan entre el vertice superior y el inferior, hay una compuerta
		if (((*it)->get_posicion_x() <= x)&&((*it)->get_posicion_x()+TAMANIO >= x)){
			if (((*it)->get_posicion_y() <= y)&&((*it)->get_posicion_y()+TAMANIO >= y)){
				auxiliar = (*it);
				encontrado = true;
			}
		}
		it++;
	}
	return auxiliar;
}


Entrada* VistaCompuerta::get_entrada(int x, int y){
	Entrada* auxiliar = NULL;
	unsigned int i;
	std::list< Entrada* > lista_entrada = circuito->get_lista_entradas();
	std::list< Entrada* >::iterator it = lista_entrada.begin();
	bool encontrado = false;
	for (i = 0; ((i < lista_entrada.size())&&(!encontrado)); i++){
		//Si x, y, estan entre el vertice superior y el inferior, hay una entrada
		if (((*it)->get_posicion_x() <= x)&&((*it)->get_posicion_x()+TAMANIO >= x)){
			if (((*it)->get_posicion_y() <= y)&&((*it)->get_posicion_y()+TAMANIO >= y)){
				auxiliar = (*it);
				encontrado = true;
			}
		}
		it++;
	}
	return auxiliar;
}




Salida* VistaCompuerta::get_salida(int x, int y){
	Salida* auxiliar = NULL;
	unsigned int i;
	std::list< Salida* > lista_salida = circuito->get_lista_salidas();
	std::list< Salida* >::iterator it = lista_salida.begin();
	bool encontrado = false;
	for (i = 0; ((i < lista_salida.size())&&(!encontrado)); i++){
		//Si x, y, estan entre el vertice superior y el inferior, hay una salida
		if (((*it)->get_posicion_x() <= x)&&((*it)->get_posicion_x()+TAMANIO >= x)){
			if (((*it)->get_posicion_y() <= y)&&((*it)->get_posicion_y() +TAMANIO >= y)){
				auxiliar = (*it);
				encontrado = true;
			}
		}
		it++;
	}
	return auxiliar;
}


ElementoExterno* VistaCompuerta::get_elem_externo(int x, int y){
	ElementoExterno* auxiliar = NULL;
	unsigned int i;
	std::list< ElementoExterno * > lista_externos = circuito->get_lista_elementos_externos();
	std::list< ElementoExterno* >::iterator it = lista_externos.begin();
	bool encontrado = false;
	int ancho;
	int alto;
	for (i = 0; ((i < lista_externos.size())&&(!encontrado)); i++){
		((*it)->calcular_ancho(&ancho, &alto));
		//Si x, y, estan entre el vertice superior y el inferior, hay un elemento externo
		if (((*it)->get_posicion_x() <= x)&&((*it)->get_posicion_x()+ancho >= x)){
			if (((*it)->get_posicion_y()<=y)&&((*it)->get_posicion_y()+alto >= y)){
				auxiliar = (*it);
				encontrado = true;
			}
		}
		it++;
	}
	return auxiliar;
}


bool VistaCompuerta::hay_elementos(){
	bool resultado = false;
	if (!circuito->get_lista_compuertas().empty())
		resultado = true;
	else if (!circuito->get_lista_entradas().empty())
		resultado = true;
	else if (!circuito->get_lista_salidas().empty())
		resultado = true;
	else if (!circuito->get_lista_elementos_externos().empty())
		resultado = true;
	return resultado;
}


void VistaCompuerta::pintar_en_contexto (Gtk::DrawingArea* area, Cairo::RefPtr<Cairo::Context> cr){
	repintar_compuertas_en_context(cr);
	repintar_entrada(cr);
	repintar_elem_externos(cr);
	repintar_salida(cr);
}

VistaCompuerta::~VistaCompuerta() {

}
